﻿using PlutoStudio.Configuration.Startup;
using PlutoStudio.Dependency;
using PlutoStudio.Localization;
using PlutoStudio.Reflection;
using PlutoStudio.Runtime.Session;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace PlutoStudio.Authorization
{
    internal class AuthorizationHelper : IAuthorizationHelper, ITransientDependency
    {
        public ISession Session { get; set; }
        public IPermissionChecker PermissionChecker { get; set; }

        public ILocalizationManager LocalizationManager { get; set; }

        private readonly IAuthorizationConfiguration _authConfiguration;

        public AuthorizationHelper( IAuthorizationConfiguration authConfiguration)
        {
            _authConfiguration = authConfiguration;
            Session = NullSession.Instance;
            PermissionChecker = NullPermissionChecker.Instance;
            LocalizationManager = NullLocalizationManager.Instance;
        }

        public async Task AuthorizeAsync(IEnumerable<IAuthorizeAttribute> authorizeAttributes)
        {
            if (!_authConfiguration.IsEnabled)
            {
                return;
            }

            if (!Session.UserId.HasValue)
            {
                throw new AuthorizationException(
                    LocalizationManager.GetString(Consts.LocalizationSourceName, "CurrentUserDidNotLoginToTheApplication")
                    );
            }

            foreach (var authorizeAttribute in authorizeAttributes)
            {
                await PermissionChecker.AuthorizeAsync(authorizeAttribute.RequireAllPermissions, authorizeAttribute.Permissions);
            }
        }

        public async Task AuthorizeAsync(MethodInfo methodInfo, Type type)
        {
            await CheckPermissions(methodInfo, type);
        }

        private async Task CheckPermissions(MethodInfo methodInfo, Type type)
        {
            if (!_authConfiguration.IsEnabled)
            {
                return;
            }

            if (AllowAnonymous(methodInfo, type))
            {
                return;
            }

            var authorizeAttributes =
                ReflectionHelper
                    .GetAttributesOfMemberAndType(methodInfo, type)
                    .OfType<IAuthorizeAttribute>()
                    .ToArray();

            if (!authorizeAttributes.Any())
            {
                return;
            }

            await AuthorizeAsync(authorizeAttributes);
        }

        private static bool AllowAnonymous(MemberInfo memberInfo, Type type)
        {
            return ReflectionHelper
                .GetAttributesOfMemberAndType(memberInfo, type)
                .OfType<IAllowAnonymousAttribute>()
                .Any();
        }
    }
}
